#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>

// 关键参数设置 - 学生可以修改这些值进行实验
#define BUFFER_SIZE 5       // 缓冲区大小
#define ITEMS_PER_PRODUCER 3  // 每个生产者生产的产品数
#define ITEMS_PER_CONSUMER 2  // 每个消费者消费的产品数
#define NUM_PRODUCERS 2     // 生产者数量
#define NUM_CONSUMERS 3     // 消费者数量

// 全局共享变量
int buffer[BUFFER_SIZE];  // 循环缓冲区
int in = 0;               // 生产位置
int out = 0;              // 消费位置
int count = 0;            // 缓冲区中当前的产品数量
int total_produced = 0;   // 已生产项目计数
int total_consumed = 0;   // 已消费项目计数
    
// 使用POSIX信号量进行同步
sem_t empty;              // 空位信号量
sem_t full;               // 满位信号量
sem_t mutex;              // 互斥信号量

// 函数原型
void initialize_synchronization();
void cleanup_synchronization();
void *producer_thread(void *arg);
void *consumer_thread(void *arg);
void print_buffer_status();

// P操作原语 - 等待资源/减少信号量
void sem_p(sem_t *sem) {
    // TODO: 实现P操作原语(wait)
    // 提示: 使用sem_wait函数减少信号量值
    
}

// V操作原语 - 释放资源/增加信号量
void sem_v(sem_t *sem) {
    // TODO: 实现V操作原语(signal)
    // 提示: 使用sem_post函数增加信号量值
    
}

int main(int argc, char *argv[]) {
    pthread_t *producers, *consumers;
    int i;
    
    printf("生产者消费者问题 (多线程版本)\n");
    printf("参数设置: 缓冲区大小=%d, 生产者=%d, 消费者=%d\n", 
           BUFFER_SIZE, NUM_PRODUCERS, NUM_CONSUMERS);
    printf("每个生产者生产%d个产品, 每个消费者消费%d个产品\n",
           ITEMS_PER_PRODUCER, ITEMS_PER_CONSUMER);
    printf("总共需生产: %d, 总共需消费: %d\n",
           NUM_PRODUCERS * ITEMS_PER_PRODUCER, NUM_CONSUMERS * ITEMS_PER_CONSUMER);

    // 初始化随机数生成器
    srand(time(NULL));
    
    // 初始化信号量
    initialize_synchronization();
    
    // 分配线程数组
    producers = (pthread_t *)malloc(NUM_PRODUCERS * sizeof(pthread_t));
    consumers = (pthread_t *)malloc(NUM_CONSUMERS * sizeof(pthread_t));
    
    // 创建生产者线程
    for (i = 0; i < NUM_PRODUCERS; i++) {
        // 使用i+1作为线程ID传递
        pthread_create(&producers[i], NULL, producer_thread, (void*)(intptr_t)(i + 1));
    }
    
    // 创建消费者线程
    for (i = 0; i < NUM_CONSUMERS; i++) {
        // 使用i+1作为线程ID传递
        pthread_create(&consumers[i], NULL, consumer_thread, (void*)(intptr_t)(i + 1));
    }
    
    // 等待所有线程结束
    for (i = 0; i < NUM_PRODUCERS; i++) {
        pthread_join(producers[i], NULL);
        printf("生产者线程 #%d 已结束\n", i+1);
    }
    
    for (i = 0; i < NUM_CONSUMERS; i++) {
        pthread_join(consumers[i], NULL);
        printf("消费者线程 #%d 已结束\n", i+1);
    }
    
    // 显示结果
    printf("\n========== 结果汇总 ==========\n");
    printf("总共生产: %d 个产品\n", total_produced);
    printf("总共消费: %d 个产品\n", total_consumed);
    
    // 清理资源
    cleanup_synchronization();
    free(producers);
    free(consumers);
    
    printf("所有线程已成功完成！\n");
    return 0;
}

// 初始化信号量
void initialize_synchronization() {
    // TODO: 初始化三个信号量(empty, full, mutex)
    // 提示: 使用sem_init函数初始化POSIX信号量
    // empty初始值应为BUFFER_SIZE, full初始值为0, mutex初始值为1
    
}

// 清理信号量
void cleanup_synchronization() {
    // TODO: 销毁三个信号量
    // 提示: 使用sem_destroy函数销毁POSIX信号量
    
}

// 打印缓冲区状态的函数
void print_buffer_status() {
    int i;
    
    printf("缓冲区: [");
    
    for (i = 0; i < BUFFER_SIZE; i++) {
        // 显示指针位置标记
        if (i == in && i == out) {
            printf("IO");
        } else if (i == in) {
            printf(" I");
        } else if (i == out) {
            printf(" O");
        } else {
            printf("  ");
        }
        
        // 直接显示当前位置的缓冲区内容
        if ((out <= in && (i >= out && i < in)) || 
            (out > in && (i >= out || i < in))) {
            // 有效缓冲区范围内
            printf("%3d ", buffer[i]);
        } else {
            // 空缓冲区位置
            printf("... ");
        }
    }
    
    printf("] 使用率: %d/%d\n", count, BUFFER_SIZE);
}

// 生产者线程函数
void *producer_thread(void *arg) {
    int id = (intptr_t)arg;  // 直接从参数中获取ID
    int item;
    
    printf("生产者线程 #%d 开始运行\n", id);
    
    // TODO: 在此线程中找到合适位置添加信号量操作，实现生产者-消费者模式的同步控制
    // 提示: 
    // 1. 生产者需要等待空位，获取互斥访问，添加产品后释放互斥访问并增加满位
    // 2. 使用sem_p和sem_v函数分别实现P操作(wait)和V操作(signal)
    // 3. 需要操作的信号量有: empty, mutex, full
    
    for (int i = 0; i < ITEMS_PER_PRODUCER; i++) {
        // 生产一个产品 (使用生产者ID作为特征)
        item = (id * 100) + (i + 1);
        
        // 临界区 - 放入产品到缓冲区
        buffer[in] = item;
        in = (in + 1) % BUFFER_SIZE;
        count++;
        printf("生产者 #%d: 在位置 %d 放入产品 %d [总计: %d]\n", 
               id, (in == 0) ? BUFFER_SIZE - 1 : in - 1, 
               item, ++total_produced);
        
        // 打印缓冲区状态
        print_buffer_status();
        
        // 随机休眠一段时间
        usleep(rand() % 500000);
    }
    
    printf("生产者 #%d: 已完成所有生产任务\n", id);
    return NULL;
}

// 消费者线程函数
void *consumer_thread(void *arg) {
    int id = (intptr_t)arg;  // 直接从参数中获取ID
    int item;
    
    printf("消费者线程 #%d 开始运行\n", id);
    
    // TODO: 在此线程中找到合适位置添加信号量操作，实现生产者-消费者模式的同步控制
    // 提示: 
    // 1. 消费者需要等待满位，获取互斥访问，取出产品后释放互斥访问并增加空位
    // 2. 使用sem_p和sem_v函数分别实现P操作(wait)和V操作(signal)
    // 3. 需要操作的信号量有: full, mutex, empty
    
    for (int i = 0; i < ITEMS_PER_CONSUMER; i++) {
        // 临界区 - 从缓冲区取出产品
        item = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        count--;
        printf("消费者 #%d: 从位置 %d 取出产品 %d [总计: %d]\n", 
               id, (out == 0) ? BUFFER_SIZE - 1 : out - 1, 
               item, ++total_consumed);
        
        // 打印缓冲区状态
        print_buffer_status();
        
        // 随机休眠一段时间
        usleep(rand() % 800000);
    }
    
    printf("消费者 #%d: 已完成所有消费任务\n", id);
    return NULL;
}
